use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json::from_reader;
use std::path::Path;
use std::sync::RwLock;


#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigService {
    pub app: AppConfig,
    pub db:DBConfig,
    pub cookie:CookieConfig
}

impl ConfigService {
    pub fn from_file(path: &Path) -> Option<ConfigService> {
        std::fs::File::open(path)
            .ok()
            .map(|f| {
                let config: Option<ConfigService> = from_reader(f).map_err(|e|{
                    println!("parse error:{:?}",e);
                    e
                }).ok();
                config
            })
            .flatten()
    }
    pub fn from_default_file() -> ConfigService {
        let mut service: Option<ConfigService> = None;
        for path in ["./config.json", "./config.default.json"].iter() {
            if let Some(loaded_service) = Self::from_file(path.as_ref()) {
                service.replace(loaded_service);
                break;
            }
        }
        service.unwrap()
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AppConfig {
    port: Option<u32>,
    host: Option<String>,
}

#[derive(Debug,Serialize,Deserialize)]
pub struct DBConfig {
    port:Option<u32>,
    host:Option<String>,
    database:String,
    username:Option<String>,
    password:Option<String>,
    max_connection:Option<u32>,
}
#[derive(Debug,Serialize,Deserialize)]
pub struct CookieConfig {
    name:Option<String>,
    private_key:Option<Vec<u8>>
}
impl AppConfig {
    pub fn get_port(&self) -> u32 {
        self.port.clone().unwrap_or(8081)
    }
    pub fn get_host(&self) -> String {
        self.host.clone().unwrap_or("localhost".into())
    }
}
impl DBConfig {
    pub fn get_port(&self) -> u32 {
        self.port.clone().unwrap_or(3306)
    }
    pub fn get_host(&self) -> String {
        self.host.clone().unwrap_or("localhost".into())
    }
    pub fn get_username(&self) -> String {
        self.username.clone().unwrap_or("root".into())
    }
    pub fn get_password(&self) -> String {
        self.password.clone().unwrap_or("root".into())
    }
    pub fn get_database(&self) -> String {
        self.database.clone()
    }
    pub fn get_max_connection(&self) -> u32 {
        self.max_connection.clone().unwrap_or(10)
    }
}
impl CookieConfig {
    pub fn get_name(&self) -> String {
        self.name.clone().unwrap_or("ShimmerClub".into())
    }
    pub fn get_private_key(&self) -> Vec<u8> {
        if let Some(ref v) = self.private_key {
            let mut n_vec = Vec::with_capacity(32);
            n_vec.fill(0);
            let (left,_) = n_vec.split_at_mut(v.len().max(32));
            left.copy_from_slice(&v[..]);
            n_vec
        } else {
            vec![0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
        }
    }
}
impl Default for AppConfig {
    fn default() -> Self {
        AppConfig {
            port: Some(8080),
            host: Some("localhost".to_owned()),
        }
    }
}

pub static GLOBAL_CONFIG: Lazy<RwLock<ConfigService>> = Lazy::new(|| {
    let config = ConfigService::from_default_file();
    RwLock::new(config)
});
#[macro_export]
macro_rules! global_config {
    () => {
        &$crate::services::config_service::GLOBAL_CONFIG
            .read()
            .unwrap()
    };
    ($name:ident) => {
        &$crate::services::config_service::GLOBAL_CONFIG
            .read()
            .unwrap()
            .$name
    };
}